/*
 * A sample, extra-simple block driver. Updated for kernel 2.6.31.
 *
 * (C) 2003 Eklektix, Inc.
 * (C) 2010 Pat Patterson <pat at superpat dot com>
 * Redistributable under the terms of the GNU GPL.
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>

#include <linux/kernel.h> /* printk() */
#include <linux/fs.h>     /* everything... */
#include <linux/errno.h>  /* error codes */
#include <linux/types.h>  /* size_t */
#include <linux/vmalloc.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>

#include "phaseshift.h"
#include "pf_net_mod.h"
#include "io_transfer.h"
#include "workqueue.h"

#include<linux/timer.h>
#include<linux/time.h>
#include<linux/jiffies.h>

MODULE_LICENSE("Dual BSD/GPL");
static char *Version = "0.01";

static int logical_block_size = LOGICAL_BLOCK_SIZE;

static int major_num = 0;
module_param(major_num, int, 0);
module_param(logical_block_size, int, 0);
static int nsectors = 1024; /* How big the drive is */
module_param(nsectors, int, 0);

static struct phs_device Device;


/*
 * Our request queue.
 */
static struct request_queue *Queue;

/*
 * Funkcia na inicializaciu vlastnych potrebnych veci v module
 */
static int phs_inicializator(void) {
	int err = 0;
	
	err = inicializujTransmitQueue(); // inicializuje pracovnu radu pre odosielanie paketov
	if (err == -1) return -5;	
	err = pf_s_init(); // inicializuje sietovu vrstvu
	if (err == -1) return -1;
	err = hello_init(); // posielanie hello paketu periodicke
	if (err == -1) return -2;
        err = create_nlist(); // vytvorenie node listu
        if (err == -1) return -3;
        err = pt_create_page_table(); // vytvorenie page_table
        if (err == -1) return -4;
	return 0;
}

static void phs_request(struct request_queue *q) {
	struct request *req;

	
	while ( (req = blk_fetch_request(q)) != NULL) {
		if (!blk_fs_request(req)) {
			printk (KERN_NOTICE "Skip non-CMD request\n");
			blk_end_request_all(req, -EIO);
			continue;
		}
		phs_transfer(req, &Device, blk_rq_pos(req), blk_rq_cur_sectors(req),
				  rq_data_dir(req));
	}
	return;
}

/*
 * The HDIO_GETGEO ioctl is handled in blkdev_ioctl(), which
 * calls this. We need to implement getgeo, since we can't
 * use tools such as fdisk to partition the drive otherwise.
 */
int phs_getgeo(struct block_device * block_device, struct hd_geometry * geo) {
	long size;

	/* We have no real geometry, of course, so make something up. */
	size = Device.size * (logical_block_size / KERNEL_SECTOR_SIZE);
	geo->cylinders = (size & ~0x3f) >> 6;
	geo->heads = 4;
	geo->sectors = 16;
	geo->start = 0;
	return 0;
}

/*
 * The device operations structure.
 */
static struct block_device_operations phs_ops = {
		.owner  = THIS_MODULE,
		.getgeo = phs_getgeo
};

static int __init phs_init(void) {
	int err;	

	err = phs_inicializator();
	/*
	 * Set up our internal device.
	 */
	Device.size = nsectors * logical_block_size;
	spin_lock_init(&Device.lock);
	Device.data = vmalloc(Device.size);
	if (Device.data == NULL)
		return -ENOMEM;
	/*
	 * Get a request queue.
	 */
	Queue = blk_init_queue(phs_request, &Device.lock);
	if (Queue == NULL)
		goto out;
	blk_queue_logical_block_size(Queue, logical_block_size);
	/*
	 * Get registered.
	 */
	major_num = register_blkdev(major_num, "phs");
	if (major_num <= 0) {
		printk(KERN_WARNING "phs: unable to get major number\n");
		goto out;
	}
	/*
	 * And the gendisk structure.
	 */
	Device.gd = alloc_disk(16);
	if (!Device.gd)
		goto out_unregister;
	Device.gd->major = major_num;
	Device.gd->first_minor = 0;
	Device.gd->fops = &phs_ops;
	Device.gd->private_data = &Device;
	strcpy(Device.gd->disk_name, "phs0");
	set_capacity(Device.gd, nsectors);
	Device.gd->queue = Queue;
	add_disk(Device.gd);
	
	printk(KERN_WARNING "phs: net debug\n");
	/* inicializacia modulu */

	if (err < 0) {
		printk(KERN_WARNING "phs: inicializator failed %d\n",err);
	}
	return 0;

out_unregister:
	unregister_blkdev(major_num, "phs");
out:
	vfree(Device.data);
	return -ENOMEM;
}


static void phs_ukoncenie(void) {
        hello_exit();
        destroy_page_list();
        destroy_nlist();
	pf_s_exit();
	zmazTransmitQueue();
}

static void __exit phs_exit(void)
{
	// ukoncime socket
	phs_ukoncenie();

	del_gendisk(Device.gd);
	put_disk(Device.gd);
	unregister_blkdev(major_num, "phs");
	blk_cleanup_queue(Queue);
	vfree(Device.data);
	printk(KERN_WARNING "phs: end operation\n");
}

module_init(phs_init);
module_exit(phs_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Phaseshift Team");
MODULE_DESCRIPTION("Distributed swap module for GRID at FIIT STU BA SVK");
MODULE_VERSION("0:0.002");

/*
 * MODULE_ALIAS("phaseshift");
 */

